/* $id$ */

(function(){
/*
configure:
	rows:
	cols:
	gridLength:
*/
function Game(configure){
	this.init(configure)
}
Game.prototype = {
	init: function(configure){
		var config = $.extend(this.config, configure || {});
		this.width = config.gridLength * config.rows,
		this.height = config.gridLength * config.cols + 30;
		this.paper = Raphael(20, 20, this.width, this.height);
		var dispatchers = 'Start,BeforeClick'.split(',');
		for( var i in dispatchers )
			this['on' + dispatchers[i]] = new Dispatcher();
	},
	start: function(){
		var game = this;
		var board = this.paper.rect(0, 30, this.width, this.height).attr({stroke: '#00f', fill: '#DEFFF9'})
		this.drawGrid();
		$(board.node).click(function(event){
			var l = game.config.gridLength;
			var offset = $(this).offset();
			var x = Math.floor((event.pageX - offset.left) / l),
				y = Math.floor((event.pageY - offset.top - 30) / l);
			game.boardClick(game.paper, x, y)
		})
		this.onStart.dispatch();
	},
	drawGrid: function(){
		var pathStrings = [], start = 0;
		for(start = 0; start <= this.width; start += this.config.gridLength){
			pathStrings.push('M' + start + ',30V' + this.height);
		}
		for(start = 30; start <= this.height; start += this.config.gridLength){
			pathStrings.push('M0,' + start + 'H' + this.width);
		}
		this.paper.path(/*{stroke: '#00f'},*/ pathStrings.join(''))
		//alert(pathStrings.join('\n'));
	},
	boardClick: function(paper, x, y){
		var game = this;
		this.drawItem(x, y, '#f00');
	},
	drawItem: function(x, y, color){
		var game = this;
		var l = this.config.gridLength;
		var c = this.paper.circle( x * l + l / 2, y * l + 30 + l / 2, l * 0.4 ).attr({fill: color});
		c._color = color;
		c._x = x;
		c._y = y;
		$(c.node).click(function(event){
			game.itemClick(c, x, y);
		})
	},
	itemClick: function(c, x, y){
		c.attr({fill: '#0f0'});
	},
	config: {
		gridLength: 30,
		rows: 10,
		cols: 10,
		dummy: null
	}
}

var red = '#f00';
var itemColors = [
	'blue',
	'red',
	'green',
	'yellow'
]

function FiveOrMore(config){
	this.init(config);


	this.onStart.add(this.initData, this);
	this.onStart.add(this.putThreeItems, this);
}
FiveOrMore.prototype = $.extend(true, {}, Game.prototype, {
	putThreeItems: function(){
		var x, y, i = 1, color;
		while(true){
			x = Math.floor(Math.random() * this.config.cols);
			y = Math.floor(Math.random() * this.config.rows);
			if(this.boardData[y][x])
				continue;
			color = itemColors[Math.floor(Math.random() * itemColors.length)];
			this.drawItem(x, y, color);
			this.boardData[y][x] = color;
			if( ++i > 3 ){
				break;
			}
		}

		this.remainSpace -= 3;
	},
	initData: function(){
		var i;

		this.boardData = new Array(this.config.rows);
		for( i = 0; i < this.boardData.length; i++ )
			this.boardData[i] = new Array(this.config.cols);

		this.moveTable = new Array(this.config.rows);
		for( i = 0; i < this.moveTable.length; i++ )
			this.moveTable[i] = new Array(this.config.cols);

		this.remainSpace = this.config.rows * this.config.cols;
	},
	boardClick: function(paper, x, y){
		if( this.state != 'selected' ) return;
		var item = this.selectedItem,
			px = item._x,
			py = item._y
			color = item._color;

		connected = this.check_connected(px, py, x, y)

		if(!connected)
			return;
		this.selectedItem.remove();
		this.drawItem(x, y, color);
		this.boardData[y][x] = color;
		this.boardData[py][px] = null;
		this.putThreeItems();
	},
	check_connected: function(px, py, x, y){
		var i, target = {x: x, y: y};
		var direction = [{x: 0, y: 1}, {x: 0, y: -1}, {x: -1, y: 0}, {x: 1, y: 0}];
		var rows = this.config.rows, cols = this.config.cols;
		var moveTable = this.moveTable, boardData = this.boardData;
		var walked = [], path = [];

		for( var i = 0; i < this.moveTable.length; i++ )
			for( var j = 0; j < this.moveTable[i].length; j++ )
				moveTable[i][j] = false;

		function check(source, target){
			var x, y, loc;
			path.push(source);
			console.log('x: %s, y: %s', source.x, source.y)
			for( i=0; i<4; i++){
				x = source.x + direction[i].x;
				y = source.y + direction[i].y;

				if(!(x >= 0 && x < cols && y >= 0 && y < rows))
					continue;

				if(!moveTable[y][x] && !boardData[y][x]){
					moveTable[y][x] = true;
					walked.push( {x: x, y: y} );
				}
			}
			while( walked.length > 0 ){
				loc = walked.shift();
				if((loc.x == target.x && loc.y == target.y) || check( loc, target )){
					return true;
				}
				path.pop();
			}
			return false;
		}

		connected = check( {x: px, y: py}, target )
		if(connected){
			console.log('%o', path)
		}
		return connected
	},
	itemClick: function(item, x, y){
		if( this.state == 'init' ){
			this.state = 'selected';
			this.setSelect(item);
		}else if( this.state == 'selected' ){
			if( x == this.selectedItem._x && y == this.selectedItem._y ){
				this.setUnSelect(this.selectedItem);
				this.state = 'init';
			}else{
				this.setUnSelect(this.selectedItem);
				this.setSelect(item);
			}
		}
	},
	state: 'init',
	setSelect: function(item){
		this.selectedItem = item;
		this.selectedItem.attr({stroke: '#f00'});
	},
	setUnSelect: function(item){
		this.selectedItem.attr({stroke: '#000'});
		this.selectedItem = null;
	}
});
window.Game = Game;
window.FiveOrMore = FiveOrMore;


function Dispatcher(){
	this.handlers = [];
}
Dispatcher.prototype = {
	add: function (handler, context){
		this.handlers.push({'handler': handler, 'context': context || this});
	},
	dispatch: function (){
		var i, item, handler, context;
		for(i in this.handlers){
			item = this.handlers[i];
			handler = item.handler;
			context = item.context;
			try{
				handler.apply(context, arguments)
			}catch(e){
				setTimeout(function(){throw e;}, 1);
			}
		}
	}
}
})();
